/*==============================================
The Core function set for dkEngine.
==============================================*/

#ifndef _DKGL_OBJECT_H_
#define _DKGL_OBJECT_H_

#include "dkglDesignPattern.h"
#include "dkglPreRequest.h"
#include "dkglLog.h"
#include "dkglMaterial.h"
#include "dkglUtility.h"
#include "dkglVector2.h"
#include "dkglVector3.h"
#include "dkglColor.h"

#include <vector>
#include <list>
#include <map>

namespace DKGL
{

class Face
{
public:
	Face(){};
	~Face(){};

	std::vector<unsigned int> mVert_idx;
	std::vector<unsigned int> mNormal_idx;
	std::vector<unsigned int> mTexCoord_idx;

	//void draw();
};

class FaceGroup
{
public:
	FaceGroup();
private:
	Material mFrontMaterial, mBackMaterial;
};

class RotationToken
{
public:
	RotationToken(){};
	RotationToken(float d, const Vector3 axis)
	{
		mDegree = d;
		mAxis = axis;
	};

	float mDegree;
	Vector3 mAxis;
};

class TranslationToken
{
public:
	TranslationToken(){};
	TranslationToken(const Vector3& t) : mTranslation(t){};
	Vector3 mTranslation;
};

class Axes
{
public:
	Axes(){mSize = 1.0f;mLength = 4.0f; mRadius = 0.2f;};
	~Axes(){};
	void render();
	void setSize(float s)
	{mSize = s;};
private:
	float mSize;
	float mLength;
	float mRadius;
};

class BBox
{
public:
	BBox();
	void render();
	void setPos(const Vector3&);
	void setSize(float x, float y, float z);

	inline Vector3 getPosition () { return mPosition; };

private:
	void _reCalculateCoord(void);
	Vector3 mVertBuf[8];
	Vector3 mSize;
	Vector3 mPosition;
};

class BaseObject : public HeapTracked
{
public:
	static const char ENTITY_BIT;
	static const char BBOX_BIT;
	static const char AXES_BIT;

	
	BaseObject(const std::string& name = "UNKNOWN");
	virtual ~BaseObject();
	virtual void render();
	void flipVisibility(char bit);
	virtual void drawEntity(){};
	
	// affine transformation
	void rotate(float degree, const Vector3& axis,TRANS_SPACE space);
	//void rotate(float degree, float x, float y, float z, TRANS_SPACE space);
	void translate(const Vector3& axis, TRANS_SPACE space);
	void translate(float x, float y, float z, TRANS_SPACE space);
	void scale(const Vector3&);
	void scale(float x, float y, float z);
	
	// Logically movement
	void moveForward(float d);
	void moveRight(float d);
	void moveUp(float d);

	void roll(float d);
	void yaw(float d);
	void pitch(float d);

	void setPosition(const Vector3& pos);
	void setPosition(float x, float y, float z);
	Vector3 getPosition() const;
	Vector3 getWorldPosition() const;

	Vector3 localAxisInParentSpace(const Vector3& axis_in_local) const;

	Vector3 localAxisInWorldSpace(const Vector3& axis_in_local) const;

	void pushChild(BaseObject*);
	BaseObject* popChild(const std::string& name);
	void freeSelf();

	BaseObject* getChild(int index);
	BaseObject* getChild(const std::string&);

	virtual Vector3 getSize(){return Vector3::ZERO;};
	void getParentTransformation(float*) const;
	void getParentRotationPart(float*) const;
	
	inline void show(bool b = true) { mShow = b; };
	inline bool isShow() const      { return mShow; };
	inline void showAxes(bool b = true)	  { mShowAxes = b; };
	inline void showBBox(bool b = true)	  { mShowBBox = b; };
	inline void showNormal(bool b = true) { mShowNormal = b; };
	inline void setName(const std::string& name) { mName = name; };
	inline const std::string& getName(void) const
	{return mName;};

	inline void setRenderMode(RENDER_MODE mode)  { mRenderMode = mode; };

	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// Material and Lignting
	///////////////////////////////////////////////////////////////////////////////////////////////////////

	void enableLighting(bool b = true)
	{mEnableLighting = b;};

	bool isLightingEnable() const
	{return mEnableLighting;};

	virtual void setMaterial(const Material& mat, unsigned int index = 0, FACE_TYPE face = FA_FRONT){};
	virtual void setColor(const Colorf& color, unsigned int index = 0, FACE_TYPE face = FA_FRONT){};

	virtual void assignParent(BaseObject* p)
	{mParent = p;};

protected:
	void _renderChildren();
	virtual void _transformation();
	virtual void _calibrateCoord(){};
	virtual void _setupBBox(){};
	virtual void _calculateNormal(){mNeedCalculateNormal = false;};

	bool mShow;
	bool mShowAxes;
	bool mShowBBox;
	bool mShowNormal;
	bool mEnableLighting;
	//bool mEnableColorTracking;

	bool mNeedCalculateBBox;
	bool mNeedCalculateNormal;
	float mLocalMatrix[16];
	RENDER_MODE mRenderMode;

	BaseObject *mParent;
	
private:
	void _initParam();
	std::string mName;
	std::string mRenderProfileName;
	std::list<BaseObject*> mChildrenList;
	
public:
	Axes mAxes;
	BBox mBBox;
};

class Ball : public BaseObject
{
public:
	Ball(std::string name = "Unknown Ball");
	~Ball(){};

	inline void setRadius(float r)
	{
		mRadius = r;
	};

	inline float getRadius(void) const
	{
		return mRadius;
	};

	virtual void setMaterial(const Material& mat, unsigned int index = 0,
		FACE_TYPE face = FA_FRONT);
	
	virtual void setColor(const Colorf& color,unsigned int index = 0,
		FACE_TYPE face = FA_FRONT);
	
	virtual void drawEntity();

private:
	float mRadius;
	unsigned int mComplexity;
	Material	mMaterial;
	Colorf		mColor;
	
};

class XYPlane : public BaseObject
{
public:
	XYPlane();
	XYPlane(float x, float y);
	~XYPlane(){};

	void setSize(float x, float y);

	virtual void drawEntity();

protected:
	void _updateVertBuf();
	void _initTexAndNormalBuf();
private:
	Vector3 mVertBuf[4], mNormal;
	Vector2 mTexCoordBuf[4];
	float mXSize, mYSize;

};

class Cube : public BaseObject
{
public:
	Cube(const std::string& name = "Unknown Cube", float size = 1.0f);
	~Cube();

	virtual void setMaterial(const Material& mat, unsigned int index = 0, FACE_TYPE face = FA_FRONT);

	virtual void drawEntity();

	inline void setSize(float _size)
	{m_fSize = _size; _initFace(); _updateVertBuf();};


	inline float getSize() const
	{return m_fSize;};

	

protected:
	void _updateVertBuf();
private:

	void _initFace();
	void _initTexNormalBuf();

	Vector3	mVertBuf[8];
	Vector3	mNormalBuf[6];
	Vector2	mTexCoordBuf[4];

	Face		mFace[6];
	Material	mFrontMaterial[6];
	Colorf		mColor[6];

	float m_fSize;

};


//a X-Y Grid,
class Grid : public BaseObject
{
public:
	Grid(float w, float h, int seg_w, int seg_h);
	~Grid();
	void setZ(float val);
	void setZ(int x_idx, int y_idx, float val);
	void resetSize(float w, float h, int seg_w, int seg_h);

	virtual void drawEntity();

	inline int segWidth() const 
	{return mSegW;};

	inline int segHeight() const
	{return mSegH;};

	inline float height() const 
	{return mHeight;};

	inline float width() const
	{return mWidth;};

	Vector3 getGridPosition(int x_idx, int y_idx) const;

	virtual void setColor(const Colorf& color,unsigned int index = 0, FACE_TYPE face = FA_FRONT)
	{ mColor = color;  }
	virtual void setMaterial(const Material& mat, unsigned int index = 0, FACE_TYPE face = FA_FRONT)
	{ mMaterial = mat; }

protected:
	virtual void _calibrateCoord();
	virtual void _setupBBox();
	virtual void _calculateNormal();

	Material	mMaterial;
	Colorf		mColor;

	Vector3 *mNormalBuf;

//private:
	void _calculateBaryCenter(){};
	void _allocateVertBur();
	Grid();
	Vector3 *mVertBuf, mCenter;
	float mHeight, mWidth;
	int mSegH, mSegW;
	
	Vector3 mBaryCenter;
};

// A LineStrip alone X direction
class LineStrip : public BaseObject
{
public:
	LineStrip(int seg);
	LineStrip(float length, int seg);
	LineStrip(const Vector3 *vert_array, int num);
	void resetSize(float length, int seg);
	virtual ~LineStrip();
	void setXYZ(int idx, float x, float y, float z);
	void setX(int idx, float val);
	void setY(int idx, float val);
	void setZ(int idx, float val);
	virtual void drawEntity();
	void setColor(float r, float g, float b);
	const Vector3 getGridPosition(int idx) const;
	const float length() const

	{return mLength;};
	const int seg() const
	{return mSeg;};

protected:
	virtual void _setupBBox();
	virtual void _calibrateCoord();
	//virtual void _calculateNormal();
	//Vector3 *mNormalBuf;

private:
	LineStrip();
	LineStrip(LineStrip&);
	Vector3 *mVertBuf;
	
	int mSeg;
	float mLength;
	Colorf mColor;
};

}


#endif